home *** CD-ROM | disk | FTP | other *** search
- /*-
- * DEVIO.C
- *
- * The xpkdisk.device code that does the real work.
- *
- * $Id: devio.c,v 1.5 1995/04/08 20:21:52 Rhialto Exp $
- * $Log: devio.c,v $
- * Revision 1.5 1995/04/08 20:21:52 Rhialto
- * Add/correct version strings.
- *
- * Revision 1.4 1995/04/02 14:58:51 Rhialto
- * Work around horrible bug in XPK when compressing non-compressible tracks.
- * Allow users to keep old copy of track file when writing the new one fails.
- * Provide more realistic values for the disk geometry with TD_GETGEOMETRY.
- * Re-initialise more stuff (XPKD:, directories, track cache) on CMD_RESET.
- *
- * Revision 1.3 1993/11/08 13:11:15 Rhialto
- * Add RCS tags.
- *
- * This code is (C) Copyright 1989-1995 by Olaf Seibert. All rights reserved.
- * May not be used or copied without a licence.
- -*/
-
- /*
- * There is a horrible bug in the xpkmaster.library version 2.4.
- * This is the one in Xpk25Usr.lha.
- * See below fow the horrid details.
- */
- #define XPKBUG 1
-
- #include <string.h>
- #include <stdio.h>
- #include "xpkdisk.h"
- #if NOXPK
- #define XPK_MARGIN 0 /* Safety margin for output buffer */
- #define XPKERR_OK 0
- #define XPKERR_IOERROUT -4 /* Output error happened,look at Result2*/
- #else
- #define LATTICE /* Get the #pragmas as well */
- #include <libraries/xpk.h>
- #undef LATTICE
- #endif
-
- /*#undef DEBUG */
- #if DEBUG
- # include "syslog.h"
- #else
- # define debug(x)
- #endif
-
- static const char rcsId[] = "$Id: devio.c,v 1.5 1995/04/08 20:21:52 Rhialto Exp $";
-
- Prototype void UnitSeglist(void);
-
- Prototype void CMD_Read(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void CMD_Write(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Format(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void CMD_Reset(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void CMD_Update(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void CMD_Clear(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Seek(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Changenum(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Addchangeint(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Remchangeint(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Getgeometry(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Motor(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Remove(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Protstatus(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Changestate(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Rawread(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Rawwrite(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Getdrivetype(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Getnumtracks(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_Eject(struct IOStdReq *ioreq, UNIT *unit);
- Prototype void TD_(struct IOStdReq *ioreq, UNIT *unit);
-
- Prototype int DevInit(DEV *dev);
- Prototype int DevCloseDown(DEV *dev);
- Prototype UNIT *UnitInit(DEV *dev, ulong UnitNr);
- Prototype int UnitCloseDown(struct IOStdReq *ioreq, DEV *dev, UNIT *unit);
-
- struct DosLibrary *DOSBase;
- struct Library *XpkBase;
- struct IntuitionBase *IntuitionBase;
-
- /* ------------------------------------------------------------------------- */
-
- void *
- GetHead(struct MinList *list)
- {
- if ((void *) list->mlh_Head != (void *) &list->mlh_Tail)
- return list->mlh_Head;
- return NULL;
- }
-
- void *
- GetTail(struct MinList *list)
- {
- if ((void *) list->mlh_Head != (void *) &list->mlh_Tail)
- return list->mlh_TailPred;
- return NULL;
- }
-
- Prototype long min(long a, long b);
- long
- min(long a, long b)
- {
- return a < b? a: b;
- }
-
- /* ------------------------------------------------------------------------- */
-
- struct CacheTrack {
- struct MinNode trk_Node;
- short trk_Number;
- ushort trk_Refcount;
- int trk_Size;
- byte trk_Data[0];
- };
-
- #define TRK_DIRTY 0x8000 /* Bit in trk_Refcount */
-
- static const char Directory[] = XPKDISKDIR "Unit%x";
-
- int
- ReadOnly(UNIT *unit)
- {
- __aligned struct InfoData infodata;
- BPTR fl;
-
- if (fl = Lock(XPKDISKDIR, SHARED_LOCK)) {
- if (Info(fl, &infodata)) {
- unit->xu_ReadOnly = (infodata.id_DiskState != ID_VALIDATED);
- }
- UnLock(fl);
- }
- return unit->xu_ReadOnly;
- }
-
- Prototype int MakeDirectory(UNIT *unit);
-
- int
- MakeDirectory(UNIT *unit)
- {
- char dirname[TRACKNAME_LENGTH];
- BPTR fl;
-
- sprintf(dirname, Directory, unit->xu_UnitNr);
- fl = Lock(dirname, SHARED_LOCK);
- if (fl == 0) {
- if (fl = CreateDir(dirname)) {
- UnLock(fl); /* exclusive locks not invited */
- fl = Lock(dirname, SHARED_LOCK);
- }
- }
- debug(("New currentdir: %x\n", fl));
- if (fl)
- fl = CurrentDir(fl);
- debug(("Old currentdir: %x\n", fl));
- if (fl)
- UnLock(fl);
- if (!ReadOnly(unit))
- MakeSubDirs(0, unit->xu_NumTracks - 1);
- return (int)fl;
- }
-
- /* ------------------------------------------------------------------------- */
-
- long
- ReadTrack(UNIT *unit, struct CacheTrack *trk)
- {
- char filename[TRACKNAME_LENGTH];
- long res = -1;
- #if NOXPK
- BPTR fh;
- #elif DEBUG
- ULONG OutLen;
- #endif
-
- #if 0
- if (trk->trk_Number >= unit->xu_NumTracks) {
- unit->xu_NumTracks = trk->trk_Number + 1;
- MakeSubDirs(trk->trk_Number, trk->trk_Number);
- }
- #endif
- NewName(filename, trk->trk_Number);
- debug(("ReadTrack: Open %s\n", filename));
- #if NOXPK
- if (fh = Open(filename, MODE_OLDFILE)) {
- debug(("ReadTrack: Open %s succeeded\n", filename));
- res = Read(fh, trk->trk_Data, unit->xu_TrackLen);
- if (res >= 0 && res < unit->xu_TrackLen) {
- memset(trk->trk_Data + res, 0, unit->xu_TrackLen - res);
- }
- Close(fh);
- #else
- debug(("Call XpkUnpackTags\n"));
- res = XpkUnpackTags(
- XPK_OutBuf, trk->trk_Data,
- XPK_OutBufLen, unit->xu_TrackLen + XPK_MARGIN,
- #if DEBUG
- XPK_GetOutLen, &OutLen,
- #endif
- XPK_InName, filename,
- XPK_PassThru, 1L,
- TAG_DONE);
- #if DEBUG
- if (OutLen != unit->xu_TrackLen) {
- debug(("XpkUnpackTags: wrong unpacked length: %d\n", OutLen));
- }
- #endif
- if (res == XPKERR_OK) {
- #endif
- } else {
- debug(("ReadTrack: Open %s failed\n", filename));
- memset(trk->trk_Data, 0, unit->xu_TrackLen);
- }
- debug(("res = %d\n", res));
- return res;
- }
-
- long
- WriteFile(char *filename, char *buf, long length, long *ioerr)
- {
- BPTR fh;
- LONG result;
-
- if (fh = Open(filename, MODE_NEWFILE)) {
- debug(("WriteFile: Open %s succeded\n", filename));
- result = Write(fh, buf, length);
- if (result != length) {
- result = XPKERR_IOERROUT;
- if (ioerr)
- *ioerr = IoErr();
- } else {
- result = XPKERR_OK;
- }
- Close(fh);
- }
-
- return result;
- }
-
- long
- WriteTrack(UNIT *unit, struct CacheTrack *trk)
- {
- char filename[TRACKNAME_LENGTH];
- char backupname[TRACKNAME_LENGTH + 4];
- char errmsg[XPKERRMSGSIZE];
- char *errp = errmsg;
- #define RETRY 1
- #define REVERT 2
- #define ABORT 0
- int choice = ABORT;
- long res = -1;
- int ioerr = 0;
-
- if (trk->trk_Number >= unit->xu_NumTracks) {
- unit->xu_NumTracks = trk->trk_Number + 1;
- MakeSubDirs(trk->trk_Number, trk->trk_Number);
- }
-
- if ((unit->xu_CacheFlags & CACHEF_SAFEWRITE) && !CheckRipcord(unit)) {
- debug(("Set WriteErr\n"));
- unit->xu_WriteErr = TDERR_TooFewSecs;
- return -1;
- }
-
- NewName(filename, trk->trk_Number);
- debug(("WriteTrack: %s\n", filename));
-
- strcpy(backupname, filename);
- strcat(backupname, ".old");
- Rename(filename, backupname);
-
- /* Keep trying to write the track until we succeed. */
- do {
- #if !NOXPK
- #if !XPKBUG
- debug(("Call XpkPackTags %s\n", unit->xu_XPKPackMethod));
- res = XpkPackTags(
- XPK_InBuf, trk->trk_Data,
- XPK_InLen, (long) unit->xu_TrackLen,
- XPK_OutName, filename,
- XPK_PackMethod, unit->xu_XPKPackMethod,
- XPK_StepDown, 1L,
- XPK_GetError, &errmsg[0],
- TAG_DONE);
- #else
- struct XpkFH *xfh;
- long TrackLen = unit->xu_TrackLen;
- char *Data = trk->trk_Data;
- long now;
- long written;
- long save; /* This is the FIX for the UGLY BUG !! */
- /*
- * Apparently, the master library trashes the longword following
- * the input data, if that data is not compressible.
- * This is also true when using the NONE compression.
- * We can save and restore this longword, because our track
- * buffers have extra space at the end for decompressing.
- */
-
- debug(("Call XpkOpenTags %s\n", unit->xu_XPKPackMethod));
-
- res = XpkOpenTags(&xfh,
- XPK_OutName, filename,
- XPK_PackMethod, unit->xu_XPKPackMethod,
- XPK_StepDown, 1L,
- XPK_GetError, &errmsg[0],
- XPK_InLen, TrackLen,
- XPK_ChunkSize, TrackLen,
- TAG_DONE);
-
- if (res == XPKERR_OK) {
- for (written = 0; written < TrackLen; written += now, Data += now) {
- now = min(xfh->NLen, TrackLen - written);
- if (now <= 0)
- break;
-
- debug(("Call XpkWrite %d\n", now));
- save = *(long *)(&Data[now]);
- res = XpkWrite(xfh, Data, now);
- *(long *)(&Data[now]) = save;
-
- if (res != XPKERR_OK)
- break;
- }
-
- res = XpkClose(xfh);
- }
- #endif
- if (res == XPKERR_OK)
- trk->trk_Refcount &= ~TRK_DIRTY;
- else if ((ioerr = IoErr()), res != XPKERR_IOERROUT)
- #endif
- {
- write:
- res = WriteFile(filename, trk->trk_Data, unit->xu_TrackLen, &ioerr);
- if (res == XPKERR_OK) {
- trk->trk_Refcount &= ~TRK_DIRTY;
- } else {
- errp = "";
- }
- }
- } while (res != XPKERR_OK &&
- (choice = FullRetry(unit, filename, res, ioerr, errp)) == RETRY);
-
- if (res == XPKERR_OK) {
- DeleteFile(backupname);
- } else if (choice == REVERT) {
- DeleteFile(filename);
- Rename(backupname, filename);
- }
- #if 0
- else if (choice == ABORT && !(trk->trk_Refcount & TRK_DIRTY)) {
- DeleteFile(backupname);
- }
- #endif
- debug(("res = %d\n", res));
- return res;
- }
-
-
- /*
- * Find a specific track. The cache list is a Least Recently Used stack:
- * Put it on the head of the cache list. So if it is not used anymore in a
- * long time, it bubbles to the end of the list, getting a higher chance
- * of being trashed for re-use.
- */
-
- struct CacheTrack *
- FindTrackByNumber(UNIT *unit, int number)
- {
- struct CacheTrack *trk;
- struct MinNode *nexttrk;
-
- debug(("FindTrackByNumber %ld\n", (long)number));
-
- trk = (struct CacheTrack *)unit->xu_Cache.LRUList.mlh_Head;
- while (nexttrk = trk->trk_Node.mln_Succ) {
- if (trk->trk_Number == number) {
- debug((" (%lx) %lx\n", (long)trk->trk_Refcount, trk));
- Remove((struct Node *)&trk->trk_Node);
- AddHead((struct List *)&unit->xu_Cache.LRUList,
- (struct Node *)&trk->trk_Node);
- return trk;
- }
- debug(("cache %ld %lx; ", (long)trk->trk_Number, trk));
- trk = (struct CacheTrack *)nexttrk;
- }
-
- return NULL;
- }
-
- /*
- * Get a fresh cache buffer. If we are allowed more cache, we just
- * allocate memory. Otherwise, we try to find a currently unused buffer.
- * We start looking at the end of the list, which is the bottom of the LRU
- * stack. If that fails, allocate more memory anyway. Not that is likely
- * anyway, since we currently lock only one track at a time.
- */
-
- struct CacheTrack *
- NewCacheTrack(UNIT *unit)
- {
- struct CacheTrack *trk;
- struct MinNode *nexttrk;
-
- debug(("NewCacheTrack\n"));
-
- #define SIZE (sizeof(*trk) + unit->xu_TrackLen + XPK_MARGIN)
-
- if (unit->xu_CurrentCache < unit->xu_MaxCache) {
- if (trk = AllocMem(SIZE, MEMF_ANY)) {
- goto add;
- }
- }
- for (trk = (struct CacheTrack *)unit->xu_Cache.LRUList.mlh_TailPred;
- nexttrk = trk->trk_Node.mln_Pred;
- trk = (struct CacheTrack *)nexttrk) {
- if ((unit->xu_CurrentCache >= unit->xu_MaxCache) &&
- (trk->trk_Refcount == TRK_DIRTY)) {
- debug(("NewCachetrack: dump dirty trk %d\n", trk->trk_Number));
- FreeCacheTrack(unit, trk); /* Also writes it to disk */
- continue;
- }
- if (trk->trk_Refcount == 0) { /* Implies not TRK_DIRTY */
- debug(("NewCachetrk: re-use clean trk %d\n", trk->trk_Number));
- Remove((struct Node *)&trk->trk_Node);
- goto move;
- }
- }
-
- trk = AllocMem(SIZE, MEMF_ANY);
-
- if (trk) {
- add:
- trk->trk_Size = SIZE;
- unit->xu_CurrentCache++;
- move:
- AddHead((struct List *) &unit->xu_Cache.LRUList,
- (struct Node *) &trk->trk_Node);
- }
-
- debug(("NewCacheTrack: %lx\n", trk));
- return trk;
- #undef SIZE
- }
-
- /*
- * Dispose a cached track, even if it has a non-zero refcount. If it is
- * dirty, write it out.
- * If an error occurs, stop.
- */
- Prototype int FreeCacheTrack(UNIT *unit, struct CacheTrack *trk);
- int
- FreeCacheTrack(UNIT *unit, struct CacheTrack *trk)
- {
- int error;
-
- debug(("FreeCachetrk %ld\n", (long)trk->trk_Number));
-
- if (trk->trk_Refcount & ~TRK_DIRTY) {
- debug(("\n\t*** PANIC!!! Refcount not 0 (%x) !!! ***\n\n", trk->trk_Refcount));
- trk->trk_Refcount &= TRK_DIRTY;
- }
-
- if (trk->trk_Refcount & TRK_DIRTY) {
- error = WriteTrack(unit, trk);
- } else
- error = 0;
-
- if (error == 0) {
- Remove((struct Node *)&trk->trk_Node);
- FreeMem(trk, trk->trk_Size);
- unit->xu_CurrentCache--;
- }
-
- return error;
- }
-
- /*
- * Create an empty cache
- */
-
- void
- InitCache(UNIT *unit)
- {
- NewList((struct List *)&unit->xu_Cache.LRUList);
- unit->xu_MaxCache = MAX_CACHE;
- unit->xu_CurrentCache = 0;
- unit->xu_CacheDirty = 0;
- unit->xu_CacheFlags = CACHE_FLAGS;
- unit->xu_CacheTimeout = CACHE_TIMEOUT;
- }
-
- /*
- * Dispose all cached tracks, possibly writing them to disk.
- * If an error occurs, stop.
- */
-
- int
- FreeCacheList(UNIT *unit)
- {
- struct CacheTrack *trk;
- int error;
-
- debug(("FreeCacheList, %ld\n", (long)unit->xu_CurrentCache));
- while ((trk = GetHead(&unit->xu_Cache.LRUList)) &&
- (error = FreeCacheTrack(unit, trk)) == 0) {
- }
-
- if (error == 0) {
- debug(("Clear WriteErr\n"));
- unit->xu_WriteErr = 0;
- }
- return error;
- }
-
- struct CacheTrack *
- GetTrack(struct IOStdReq *ioreq, int track)
- {
- UNIT *unit;
- struct CacheTrack *trk;
-
- debug(("GetTrack %ld\n", (long)track));
- unit = (UNIT *) ioreq->io_Unit;
-
- if (trk = FindTrackByNumber(unit, track)) {
- trk->trk_Refcount++;
- return trk;
- }
-
- if (trk = NewCacheTrack(unit)) {
- trk->trk_Refcount = 1;
- trk->trk_Number = track;
- ReadTrack(unit, trk);
- return trk;
- }
-
- return NULL;
- }
-
- void
- MarkTrackDirty(UNIT *unit, struct CacheTrack *trk)
- {
- if (trk) {
- trk->trk_Refcount |= TRK_DIRTY;
- unit->xu_CacheDirty = 1;
- }
- }
-
- /*
- * Unlock a cached track. When the usage count drops to zero, which
- * implies it is not dirty, and we are over our cache quota, the sector is
- * freed. Otherwise we keep it for re-use.
- */
-
- void
- FreeTrack(UNIT *unit, struct CacheTrack *trk)
- {
- if (trk) {
- --trk->trk_Refcount;
- /*
- * If we need to dump cache then dump some long-unused track.
- */
- while (unit->xu_CurrentCache > unit->xu_MaxCache &&
- (trk = GetTail(&unit->xu_Cache.LRUList)) &&
- (trk->trk_Refcount & ~TRK_DIRTY) == 0) {
- debug(("Freetrk: dump %s trk %d\n",
- (trk->trk_Refcount & TRK_DIRTY)? "dirty" : "clean",
- trk->trk_Number));
- FreeCacheTrack(unit, trk);
- }
- }
- }
-
- /*
- * Decide if we must do an update.
- */
-
- int
- Internal_Update(UNIT *unit)
- {
- struct CacheTrack *trk;
- struct MinNode *nexttrk;
- int error = 0;
-
- debug(("Internal_Update\n"));
- /* Is the cache dirty? */
- if (unit->xu_CacheDirty == 0) {
- debug(("Cache not dirty\n"));
- goto cleartriggers;
- }
-
- /* Did we get a CMD_UPDATE, if required? */
- if ((unit->xu_CacheFlags & (CACHEF_CMDUPDATE|CACHEF_GOTCMDUPD)) == CACHEF_CMDUPDATE) {
- debug(("No required UPDATE\n"));
- return 0;
- }
-
- /* Did we get a timeout, if required? */
- if ((unit->xu_CacheFlags & (CACHEF_DELAY|CACHEF_GOTTIMEOUT)) == CACHEF_DELAY) {
- debug(("No required TIMEOUT\n"));
- return 0;
- }
-
- for (trk = (struct CacheTrack *)unit->xu_Cache.LRUList.mlh_TailPred;
- nexttrk = trk->trk_Node.mln_Pred;
- trk = (struct CacheTrack *)nexttrk) {
- if (trk->trk_Refcount & TRK_DIRTY) {
- if (error = WriteTrack(unit, trk)) {
- goto end; /* Don't clear update conditions */
- }
- }
- }
-
- unit->xu_CacheDirty = 0;
- if (error == 0) {
- debug(("Clear WriteErr\n"));
- unit->xu_WriteErr = 0;
- }
- cleartriggers:
- unit->xu_CacheFlags &= ~(CACHEF_GOTCMDUPD | CACHEF_GOTTIMEOUT);
- end:
- return error;
- }
-
- /* ------------------------------------------------------------------------- */
-
- /*
- * Read zero or more sectors from the disk and copy them into the user's
- * buffer.
- */
-
- void
- CMD_Read(struct IOStdReq *ioreq, UNIT *unit)
- {
- int track;
- byte *userbuf;
- long length;
- long offset;
- struct CacheTrack *trk;
- int error;
-
- debug(("CMD_Read "));
- userbuf = (byte *) ioreq->io_Data;
- length = ioreq->io_Length;
- offset = ioreq->io_Offset;
- debug(("userbuf %08lx off %ld len %ld\n", userbuf, offset, length));
-
- track = offset / unit->xu_TrackLen;
- offset = offset % unit->xu_TrackLen;
- debug(("Tr=%ld Offset=%ld\n", (long)track, (long)offset));
-
- ioreq->io_Actual = length;
- error = TDERR_NoError;
-
- if (length <= 0)
- goto end;
-
- if (offset != 0) {
- /* Handle non-track part first */
- ulong l;
-
- trk = GetTrack(ioreq, track);
- if (trk == NULL) {
- error = TDERR_NoSecHdr;
- goto end;
- }
- l = min(length, unit->xu_TrackLen - offset);
-
- CopyMem(trk->trk_Data + offset, userbuf, l);
- userbuf += l;
- length -= l;
- track++;
- FreeTrack(unit, trk);
- }
-
- while (length > 0) {
- ulong l;
-
- trk = GetTrack(ioreq, track);
- if (trk == NULL) {
- error = TDERR_NoSecHdr;
- goto end;
- }
- l = min(length, unit->xu_TrackLen);
-
- CopyMem(trk->trk_Data, userbuf, l);
- userbuf += l;
- length -= l;
- track++;
- FreeTrack(unit, trk);
- }
-
- end:
- ioreq->io_Error = error;
- if (error != TDERR_NoError)
- ioreq->io_Actual = 0;
- TermIO(ioreq);
- }
-
- void
- CMD_Write(struct IOStdReq *ioreq, UNIT *unit)
- {
- int track;
- byte *userbuf;
- long length;
- long offset;
- struct CacheTrack *trk;
- int error;
-
- debug(("CMD_Write "));
-
- if (unit->xu_ReadOnly) {
- error = TDERR_WriteProt;
- goto end;
- }
-
- userbuf = (byte *) ioreq->io_Data;
- length = ioreq->io_Length;
- offset = ioreq->io_Offset;
- debug(("userbuf %08lx off %ld len %ld\n", userbuf, offset, length));
-
- track = offset / unit->xu_TrackLen;
- offset = offset % unit->xu_TrackLen;
- debug(("Tr=%ld Offset=%ld\n", (long)track, (long)offset));
-
- ioreq->io_Actual = length;
- error = TDERR_NoError;
-
- if (length <= 0)
- goto end;
-
- if (offset != 0) {
- /* Handle non-track aligned part first */
- ulong l;
-
- trk = GetTrack(ioreq, track);
- if (trk == NULL) {
- error = TDERR_NoSecHdr;
- goto end;
- }
- l = min(length, unit->xu_TrackLen - offset);
-
- CopyMem(userbuf, trk->trk_Data + offset, l);
- userbuf += l;
- length -= l;
- track++;
- MarkTrackDirty(unit, trk);
- FreeTrack(unit, trk);
- }
-
- while (length > 0) {
- ulong l;
-
- trk = GetTrack(ioreq, track);
- if (trk == NULL) {
- error = TDERR_NoMem;
- goto end;
- }
- l = min(length, unit->xu_TrackLen);
-
- CopyMem(userbuf, trk->trk_Data, l);
- userbuf += l;
- length -= l;
- track++;
- MarkTrackDirty(unit, trk);
- FreeTrack(unit, trk);
- }
-
- end:
- if (error == 0 && unit->xu_WriteErr != 0) {
- /* Propagate earlier write error(s) of previous tracks */
- debug(("Use & Clear WriteErr\n"));
- error = unit->xu_WriteErr;
- unit->xu_WriteErr = 0;
- }
- ioreq->io_Error = error;
- if (error != TDERR_NoError)
- ioreq->io_Actual = 0;
- TermIO(ioreq);
-
- StartTimeout(unit);
- }
-
- void
- TD_Format(struct IOStdReq *ioreq, UNIT *unit)
- {
- debug(("CMD_Format "));
- /* keep it simple for now */
- CMD_Write(ioreq, unit);
- }
-
- void
- CMD_Reset(struct IOStdReq *ioreq, UNIT *unit)
- {
- debug(("CMD_Reset\n"));
- #if 1
- /*
- * Somebody may have changed XPKD: to point somewhere else,
- * so push the cache and then re-establish the current dir.
- */
- FreeCacheList(unit); /* Just to be sure */
- MakeDirectory(unit);
- #endif
- MagicInit(unit); /* Get mountlist info and user prefs */
- if (!ReadOnly(unit) && (unit->xu_CacheFlags & CACHEF_SAFEWRITE))
- MakeRipcord(unit);
- TermIO(ioreq);
- }
-
- void
- CMD_Update(struct IOStdReq *ioreq, UNIT *unit)
- {
- debug(("CMD_Update\n"));
- unit->xu_CacheFlags |= CACHEF_GOTCMDUPD;
- StartTimeout(unit);
- ioreq->io_Error = Internal_Update(unit);
- TermIO(ioreq);
- }
-
- void
- CMD_Clear(struct IOStdReq *ioreq, UNIT *unit)
- {
- debug(("CMD_Clear\n"));
- ioreq->io_Error = FreeCacheList(unit);
- TermIO(ioreq);
- }
-
- void
- TD_Motor(struct IOStdReq *ioreq, UNIT *unit)
- {
- debug(("TD_Motor\n"));
- ioreq->io_Actual = 1; /* Motor is running */
- TermIO(ioreq);
- }
-
- void
- TD_Return0(struct IOStdReq *ioreq, UNIT *unit)
- {
- debug(("TD_Seek, TD_Changenum, TD_Changestate\n"));
- ioreq->io_Actual = 0;
- TermIO(ioreq);
- }
-
- void
- TD_Protstatus(struct IOStdReq *ioreq, UNIT *unit)
- {
- debug(("TD_Protstatus\n"));
- ioreq->io_Actual = ReadOnly(unit);
- TermIO(ioreq);
- }
-
- void
- TD_Getnumtracks(struct IOStdReq *ioreq, UNIT *unit)
- {
- debug(("TD_Getnumtracks\n"));
- ioreq->io_Actual = unit->xu_NumTracks; /* a guess */
- TermIO(ioreq);
- }
-
- /*
- * Handle disk change interrupts. However, our disks never get taken
- * out of their drives.
- */
-
- void
- TD_Addchangeint(struct IOStdReq *ioreq, UNIT *unit)
- {
- Enqueue((struct List *)&unit->xu_ChangeIntList, &ioreq->io_Message.mn_Node);
- ioreq->io_Flags &= ~IOF_QUICK; /* So we call ReplyMsg instead of
- * TermIO */
- /* Note no TermIO */
- }
-
- void
- TD_Remchangeint(struct IOStdReq *ioreq, UNIT *unit)
- {
- struct IOStdReq *intreq;
-
- intreq = (struct IOStdReq *) ioreq->io_Data;
- Remove(&intreq->io_Message.mn_Node);
- ReplyMsg(&intreq->io_Message); /* Quick bit always cleared */
- ioreq->io_Error = 0;
- TermIO(ioreq);
- }
-
- void
- TD_Getgeometry(struct IOStdReq *ioreq, UNIT *unit)
- {
- #if defined(TD_GETGEOMETRY)
- struct DriveGeometry *dg;
- short numtracks;
-
- debug(("TD_Getgeometry\n"));
- dg = (struct DriveGeometry *)ioreq->io_Data;
-
- numtracks = unit->xu_NumTracks;
- dg->dg_SectorSize = XD_BPS;
-
- dg->dg_Cylinders = numtracks;
- dg->dg_CylSectors = unit->xu_TrackLen / XD_BPS;
-
- dg->dg_TotalSectors = numtracks * dg->dg_CylSectors;
-
- dg->dg_Heads = 1;
- dg->dg_TrackSectors = dg->dg_CylSectors;
-
- dg->dg_BufMemType = MEMF_PUBLIC;
- dg->dg_DeviceType = DG_DIRECT_ACCESS;
- dg->dg_Flags = 0; /* not DGF_REMOVABLE */
- #else
- ioreq->io_Error = IOERR_NOCMD;
- #endif
- TermIO(ioreq);
- }
-
- /* ------------------------------------------------------------------------- */
-
- #define ROUNDS 2
-
- void
- StartTimer(UNIT *unit)
- {
- WaitIO(&unit->xu_TimeReq.tr_node);
- unit->xu_TimeReq.tr_node.io_Command = TR_ADDREQUEST;
- unit->xu_TimeReq.tr_time.tv_secs = unit->xu_CacheTimeout / ROUNDS;
- unit->xu_TimeReq.tr_time.tv_micro =
- (unit->xu_CacheTimeout % ROUNDS) * (1000000 / ROUNDS);
- SendIO(&unit->xu_TimeReq.tr_node);
- }
-
- Prototype void PollTimer(UNIT *unit);
- void
- PollTimer(UNIT *unit)
- {
- if (CheckIO(&unit->xu_TimeReq.tr_node)) {
- if (--unit->xu_TimeoutCounter <= 0) {
- unit->xu_CacheFlags |= CACHEF_GOTTIMEOUT;
- (void)Internal_Update(unit);
- } else {
- StartTimer(unit);
- }
- }
- }
-
- Prototype void StartTimeout(UNIT *unit);
-
- void
- StartTimeout(UNIT *unit)
- {
- unit->xu_TimeoutCounter = ROUNDS;
- if (CheckIO(&unit->xu_TimeReq.tr_node))
- StartTimer(unit);
- }
-
- int
- DevInit(DEV *dev)
- {
- debug(("DevInit: open dos\n"));
- DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 0);
-
- debug(("done DevInit\n"));
- return 1; /* Initializing succeeded */
-
- abort:
- return DevCloseDown(dev);
- }
-
- int
- DevCloseDown(dev)
- DEV *dev;
- {
- if (DOSBase) {
- CloseLibrary((struct Library *)DOSBase);
- DOSBase = NULL;
- }
- return 0; /* Now unitialized */
- }
-
- ulong
- InitMsgPort(struct MsgPort *p)
- {
- p->mp_SigTask = FindTask(NULL);
- p->mp_SigBit = AllocSignal(-1);
- p->mp_Flags = PA_SIGNAL;
- Forbid();
- /*
- * We must Forbid() here to prevent a race condition. That is also
- * sufficient, since interrupts are not allowed to call BeginIO().
- */
- if (p->mp_MsgList.lh_Head == NULL)
- NewList(&p->mp_MsgList);
- Permit();
-
- return 1L << p->mp_SigBit;
- }
-
- UNIT *
- UnitInit(dev, UnitNr)
- DEV *dev;
- ulong UnitNr;
- {
- UNIT *unit;
- struct Task *task;
- struct MsgPort *p;
-
- unit = AllocMem((long) sizeof (UNIT), MEMF_PUBLIC | MEMF_CLEAR);
- if (unit == NULL)
- return NULL;
-
- unit->xu_UnitNr = UnitNr;
- /*
- * Now create the Unit process. Remember that it won't start running
- * since we are Forbid()den. But just to be sure, we Forbid() again.
- */
- debug(("call CreateProc %x (%x)\n",
- MKBADDR(UnitSeglist), UnitSeglist));
- Forbid();
- p = CreateProc(DevName, TASKPRI, MKBADDR(UnitSeglist), TASKSTACK);
- if (p == NULL) {
- Permit();
- debug(("*** No Unit process!!!\n"));
- goto abort;
- }
- task = (struct Task *)(((char *)p) - offsetof(struct Process, pr_MsgPort));
- task->tc_UserData = (APTR) unit;
-
- unit->xu_Port.mp_Flags = PA_IGNORE;
- unit->xu_Port.mp_SigTask = task;
- NewList(&unit->xu_Port.mp_MsgList);
-
- Permit();
- debug(("task: %lx\n", task));
-
- return unit;
-
- abort:
- UnitCloseDown(NULL, dev, unit);
- return NULL;
- }
-
- Prototype ulong UnitInit2(UNIT *unit);
- ulong
- UnitInit2(UNIT *unit)
- {
- ulong waitmask;
-
- waitmask = InitMsgPort(&unit->xu_Port);
- waitmask |= InitMsgPort(&unit->xu_TimerPort);
-
- unit->xu_TimeReq.tr_node.io_Message.mn_ReplyPort = &unit->xu_TimerPort;
- if (OpenDevice("timer.device", UNIT_VBLANK, &unit->xu_TimeReq.tr_node, 0))
- return 0;
- unit->xu_TimeReq.tr_node.io_Flags = IOF_QUICK;
-
- NewList((struct List *)&unit->xu_ChangeIntList);
- unit->xu_TrackLen = XD_TRACKLEN;
- strncpy(unit->xu_XPKPackMethod, PACKING_METHOD, 9);
- InitCache(unit);
- MagicInit(unit); /* Get mountlist info and user prefs */
- MakeDirectory(unit);
- if (!unit->xu_ReadOnly && (unit->xu_CacheFlags & CACHEF_SAFEWRITE))
- MakeRipcord(unit);
-
- #if !NOXPK
- XpkBase = OpenLibrary("xpkmaster.library", 0);
- if (XpkBase == NULL)
- return 0;
- #endif
- IntuitionBase = OpenLibrary("intuition.library", 33);
- if (IntuitionBase == NULL)
- return 0;
-
- return waitmask;
- }
-
- Prototype void UnitCloseDown2(UNIT *unit);
- void
- UnitCloseDown2(UNIT *unit)
- {
- /*
- * There may still be dirty tracks due to delayed updates.
- * If an error occurs while writing the tracks out, we currently
- * lose the memory.
- */
- (void)FreeCacheList(unit);
- {
- struct IORequest *ioreq;
- while (ioreq = (struct IORequest *)
- RemHead((struct List *)&unit->xu_ChangeIntList)) {
- ReplyMsg(&ioreq->io_Message);
- }
- }
-
- UnLock(CurrentDir(0));
-
- AbortIO(&unit->xu_TimeReq.tr_node);
- WaitIO(&unit->xu_TimeReq.tr_node);
- CloseDevice(&unit->xu_TimeReq.tr_node);
- if (IntuitionBase)
- CloseLibrary(IntuitionBase);
- if (XpkBase)
- CloseLibrary(XpkBase);
- }
-
- int
- UnitCloseDown(struct IOStdReq *ioreq, DEV *dev, UNIT *unit)
- {
- /*
- * Get rid of the Unit's task. We know this is safe because the unit
- * has an open count of zero, so it is 'guaranteed' not in use.
- */
-
- if (unit->xu_Port.mp_SigTask) {
- struct IORequest io;
-
- io.io_Message.mn_ReplyPort = (struct MsgPort *)FindTask(NULL);
- io.io_Command = CMD_Die;
- unit->xu_Flags &= ~UNITF_STOPPED;
- debug(("Asking task to die\n"));
- PutMsg(&unit->xu_Port, &io.io_Message);
- do {
- Wait(SIGF_SINGLE);
- debug(("Someone exhaled...\n"));
- } while (io.io_Message.mn_Node.ln_Type != NT_REPLYMSG);
- debug(("That was the last gasp!\n"));
- }
- FreeMem(unit, (long) sizeof (UNIT));
-
- return 0; /* Now unitialized */
- }
-
- /* ------------------------------------------------------------------------- */
-